package com.lucanjun.cn.springboot.common.annotation.RSA;

import com.alibaba.fastjson.JSON;
import com.lucanjun.cn.springboot.business.model.dto.base.RSADto;
import com.lucanjun.cn.springboot.common.constants.RequestHeaderConstant;
import com.lucanjun.cn.springboot.common.utils.RSAUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;

/**
 * @author lucanjun
 * @version 1.0.0
 * @title RSAAspect
 * @create 2024-12-19 16:55:19
 * @description RSA切面类
 */
@Slf4j
@Aspect
@Component
public class RSAAspect {

    public RSAAspect() {

    }

    @Pointcut("@annotation(com.lucanjun.cn.springboot.common.annotation.RSA.RSA)")
    public void rsaPointCut() {

    }

    @Pointcut("@annotation(com.lucanjun.cn.springboot.common.annotation.RSA.RSA)")
    public void rsaErrorPointCut() {

    }

    @Around("rsaPointCut()")
    public Object invokeRSAPointCut(ProceedingJoinPoint pjp) throws Throwable {

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String requestURI = request.getRequestURI();

        //获取当前请求系统信息
        String system = request.getHeader(RequestHeaderConstant.SYSTEM);

        Class targetClass = pjp.getTarget().getClass();

        StringBuilder builder = new StringBuilder();
        if (!targetClass.getName().contains("Oss")) {
            Object[] args = pjp.getArgs();
            for (Object arg : args) {
                builder.append(JSON.toJSONString(arg));
            }
        }

        String paramString = builder.toString();
        RSADto rsaDto = JSON.parseObject(paramString, RSADto.class);

        //验证RSA
        RSAPublicKey publicKey = RSAUtil.getPublicKey();
        RSAPrivateKey privateKey = RSAUtil.getPrivateKey();
        String decrypt = RSAUtil.decrypt(rsaDto.getParam(), privateKey);
        RSAUtil.verif(decrypt, rsaDto.getSign(), publicKey);

        Object result = pjp.proceed();

        log.info("接口请求结束，【接口请求成功】，请求用户：【{}】，请求接口的路径：【{}】，请求接口请求参数：【{}】，请求接口响应结果：【{}】",
                system,
                requestURI,
                JSON.toJSON(builder.toString()),
                JSON.toJSON(result));

        if (null == result) {
            return result;
        }
        return result;
    }

    @AfterThrowing(pointcut = "rsaErrorPointCut()", throwing = "e")
    public void invokeRSAErrorPointCut(JoinPoint joinPoint, Throwable e) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String requestURI = request.getRequestURI();

        //获取当前请求系统信息
        String system = request.getHeader(RequestHeaderConstant.SYSTEM);

        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            Object[] args = joinPoint.getArgs();
            StringBuilder builder = new StringBuilder();
            String name = method.getDeclaringClass().getName();
            if (!name.contains("Oss")) {
                for (Object arg : args) {
                    builder.append(JSON.toJSONString(arg) + ",");
                }
            }

            log.info("接口请求结束，【接口请求失败】，请求用户：【{}】，请求接口的路径：【{}】，请求接口请求参数：【{}】，请求接口响应结果：【{}】",
                    system,
                    requestURI,
                    JSON.toJSON(builder.toString()),
                    JSON.toJSON(stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace())));
        } catch (Throwable throwable) {
            throw throwable;
        }
    }

    public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
        StringBuilder stringBuilder = new StringBuilder();
        for (StackTraceElement element : elements) {
            stringBuilder.append(element).append("\n");
        }
        String message = exceptionName + ":" + exceptionMessage + "\n\t" + stringBuilder.toString();
        return message;
    }

}
